package se.webbzon.boltzmann.game.object.portal;

import java.util.Collection;

import se.webbzon.boltzmann.effect.ScreenTransitionEffect;
import se.webbzon.boltzmann.environment.AbstractEnvironment;
import se.webbzon.boltzmann.game.object.player.AbstractPlayerCharacter;
import se.webbzon.boltzmann.resource.Resources;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldeditor.DeflatableWorldObject;
import se.webbzon.oschi01.worldeditor.WorldObjectDeflation;
import se.webbzon.oschi01.worldobject.WorldObject;
import se.webbzon.oschi01.worldobject.appearance.Appearance3D;
import se.webbzon.oschi01.worldobject.appearance.SimpleAppearance3D;
import se.webbzon.oschi01.worldobject.appearance.XZPlaneAppearance3D;

public class RoadPortal extends AbstractPortal implements DeflatableWorldObject {

	// The name of the deflatable world object
	public static final String worldObjectName = "roadPortal";
	
	// The state of the portal
	private static enum State {IDLE, LEAVING, ENTERING};
	
	// The maximal range used when checking the players movement
	private static final double teleportRange = Math.sqrt(2)/2;
	
	// The maximal range of the portal
	private double range;
	
	// The state of the portal
	private State state;
	
	// The transition effect for this road portal
	private ScreenTransitionEffect transition;
	
	// The player associated to to transition
	private AbstractPlayerCharacter player;
	
	// The player blocked
	private Object blocker;
	
	/*============================================================
	Constructors
	============================================================*/

	public RoadPortal(	AbstractEnvironment env, 
						String worldAreaName, 
						byte portalId) {
		super(	WorldObject.onCreate + WorldObject.onStep, 
				createAppearance(),
				env, worldAreaName, portalId);
		setVisible(env.isDebugMode());
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	@Override public void onCreate(WorldArea world) {
		super.onCreate(world);
		range = Math.hypot(getWidth()/2, getHeight()/2);
		state = State.IDLE;
	}
	
	@Override public void onStep(WorldArea world) {
		if (state == State.ENTERING) {
			// Enter the world area via the portal
			if (transition.getDuration() <= 0) {
				player.releaseBlocker(blocker);
				player.setMotion(0, 0);
				state = State.IDLE;
			}
			
		} else if (state == State.LEAVING) {
			// Leave the world area via the portal
			if (transition.getDuration() <= 0) {
				teleport(player.getPlayer());
				state = State.IDLE;
			}
			
		} else if (!isOneWayPortal()) {
			// Check for players 
			final double portalAngle = getAngle();
			Collection<AbstractPlayerCharacter> players = getPlayers();
			synchronized (players) {
				for (AbstractPlayerCharacter player : players) {
					// Check if the player is available and within range
					if (!player.isBlocked() && distanceTo(player) < range) {
						final double 	xv = player.getXVelocity(),
										zv = player.getZVelocity();
						final double playerAngle = Math.atan2(-zv,xv);
						final double playerSpeed = Math.hypot(zv,xv);
						// Check where the player is heading
						if (playerSpeed > 0.1 ) {
							final double tr = Math.hypot(	Math.cos(playerAngle) - 
															Math.cos(portalAngle),
															Math.sin(playerAngle) - 
															Math.sin(portalAngle));
							if (tr < teleportRange) {
								// Make the player leave the room
								blocker = player.containBlocker();
								transition = 
										new ScreenTransitionEffect(	
												ScreenTransitionEffect.FADE,
												"transition_square",20);
								transition.launch(world);
								state = State.LEAVING;
								this.player = player;
							}
						}
					}
				}
			}
		}
	}
	
	@Override public void spawn(AbstractPlayerCharacter player) {
		this.player = player;
		player.setPosition(getX(),getY() + player.getHeight()/2, getZ());
		player.setFacing(getAngle() + Math.PI);
		blocker = player.containBlocker();
		player.setMotion(getAngle() + Math.PI, 1.5);
		state = State.ENTERING;
		transition = 
				new ScreenTransitionEffect(	
						ScreenTransitionEffect.FADE + ScreenTransitionEffect.REVERSED,
						"transition_square",20);
		transition.launch(getWorld());
	}
	
	@Override public String getName() {
		return worldObjectName;
	}

	@Override public WorldObjectDeflation getDeflation() {
		return new RoadPortalDeflation(0,this);
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Creates the appearance of the road portal. (Only used in
	 * debugging mode). **/
	private static final Appearance3D createAppearance() {
		SimpleAppearance3D portal = 
				new XZPlaneAppearance3D(0,SimpleAppearance3D.LAYERS,false);
		portal.setTextureSet(Resources.getTextureSet("misc_direction"));
		portal.setLayer(0, 4, 0);
		return portal;
	}

}
